package sv.com.servicio.generico;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import sv.com.action.comun.ComunAction;
import sv.com.objetos.auxiliares.Cadena;
import sv.com.objetos.auxiliares.Constantes;
import sv.com.objetos.auxiliares.Consulta;


public class DaoGenericoImpl implements DaoGenerico {


	private EntityManager em;

	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public void agregar(Object objeto) throws Exception {
		getEm().persist(objeto);
	}
	
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public List<?> cargarTodos(Class clase, String ordenarPor, String valorOrdenamiento, int pagina, int bloque) {
		StringBuilder sb=new StringBuilder();
		sb.append("FROM "+clase.getName()+" c");
		if(!Cadena.vacio(ordenarPor))
			sb.append(" order by "+ComunAction.cambiarAPunto(ordenarPor)+" "+valorOrdenamiento);
		Query consulta = getEm().createQuery(sb.toString());
		if(pagina!=Constantes.SIN_LIMITE)
			consulta.setFirstResult(bloque * (pagina-1)).setMaxResults(bloque);
		 return consulta.getResultList();
	}

	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public List<?> buscarPorPropiedades(Class clase, String propiedades, ArrayList<Object> valores, int pagina, int bloque){
		StringBuilder sb=new StringBuilder();
		sb.append("from "+clase.getName()+" c ");
		Query consulta =null;
		if(propiedades != null && propiedades.trim().length()>0){
			
			sb.append(" where ").append(propiedades);
			consulta = getEm().createQuery(sb.toString());
			int i=1;
			for(Object o:valores){
				consulta.setParameter(i++, o);
			}
		}else consulta = getEm().createQuery(sb.toString());
		if(pagina!=Constantes.SIN_LIMITE)
			consulta.setFirstResult(bloque * (pagina-1)).setMaxResults(bloque);
		return consulta.getResultList();
	}
	
	/**
	 * Realiza Busqueda HQL 
	 * @param hql
	 * @param parametros
	 * @return
	 */
	@SuppressWarnings("unchecked")
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public List realizarBusqueda(String hql, HashMap<String, Object> parametros){
		List resultado=null;
		try{
			Query consulta=getEm().createQuery(hql);
			
			if(parametros!=null){
				Iterator it=parametros.entrySet().iterator();
				while(it.hasNext()){
					Map.Entry e = (Map.Entry)it.next();
					consulta.setParameter((String)e.getKey(), e.getValue());
				}
			}
			resultado = consulta.getResultList();
		}catch (Exception e) {
			e.printStackTrace();
		}
		return resultado;
	}
	
	/**
	 * Obtiene la cantidad de registros en base a la Busqueda HQL 
	 * @param hql
	 * @param parametros
	 * @return
	 */
	@SuppressWarnings("unchecked")
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public Integer realizarBusquedaTamanoTotal(String hql, HashMap<String, Object> parametros){
		try{
			Query consulta=getEm().createQuery(hql);
			
			if(parametros!=null){
				Iterator it=parametros.entrySet().iterator();
				while(it.hasNext()){
					Map.Entry e = (Map.Entry)it.next();
					consulta.setParameter((String)e.getKey(), e.getValue());
				}
			}
			return Integer.valueOf(consulta.getSingleResult().toString());
		}catch (Exception e) {
			e.printStackTrace();
		}
		return new Integer(0);
	}
	
	
	/**Version 2.0 carga todos los datos de la clase indicada, permitiendo aplicar filtros y ordenamientos por cualquier campo relacionado a la Clase.
	 * @author dsaravia
	 * @param parametros
	 * @param page: indica el numero de la pagina para la cual mostrar los datos. Si la pagina es cero, no se pagina, mostrando todos los registros encontrados.
	 * Maximo a mostrar: Constantes.MAX_RESULTADOS
	 * @param elementsPerBlock
	 * @return lista de clazz
	 * @throws ExcepcionDeConsulta 
	 */
	@SuppressWarnings("unchecked")
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public List cargarTodosV2(Class clazz, HashMap<String, Object>parametros, int pagina, int bloque) throws Exception{
		String filtroInicial=null;
		String ordenarPor=null;
		String propiedades[]=null;
		Object valores[]=	null;
		String tiposConsulta[]=	null;
		String AND_OR=Consulta.AND;
		if(parametros!=null){
			filtroInicial=	(parametros.get("filtroInicial")!=null)		?(String)parametros.get("filtroInicial")	:null;
			ordenarPor=		(parametros.get("ordenarPor")!=null)		?(String)parametros.get("ordenarPor")		:null;
			propiedades=	(parametros.get("propiedades")!=null)		?(String[])parametros.get("propiedades")	:null;
			valores=		(parametros.get("valores")!=null)			?(Object[])parametros.get("valores")		:null;
			tiposConsulta=	(parametros.get("tiposConsulta")!=null)		?(String[])parametros.get("tiposConsulta")	:null;
			AND_OR=			(parametros.get(Consulta.AND_OR)!=null)		?((String)parametros.get(Consulta.AND_OR))	:null;
		}
		if(AND_OR==null) AND_OR=Consulta.AND;
		String consulta="";
		
		consulta="from " + clazz.getName() +" model0 ";
		if(propiedades!=null && propiedades.length>0){
			consulta+=" where ";
			if(filtroInicial!=null) consulta+=filtroInicial +" and ( ";
			int contAux=0;
			for(int i=0;i<propiedades.length;i++){
				if(propiedades[i]!=null){
					if(contAux>0) consulta+=AND_OR;
					if(tiposConsulta[i].equals(Consulta.IN) || tiposConsulta[i].equals(Consulta.NOT_IN)){
						consulta+=" trim(upper(model0."+propiedades[i]+")) "+tiposConsulta[i] +" ( "; 
						for(int j=0;j<((Object[])valores[i]).length;j++)
							if(j==0) consulta+=" ? "; else consulta+=", ?";	
						consulta+=" ) ";
					}
					else consulta+=" upper(model0."+propiedades[i]+") "+tiposConsulta[i] +" ( upper(?) )";
					contAux++;
				}
			}
			if(contAux==0) consulta+=" 1=1 ";
			if(filtroInicial!=null) consulta+=" ) ";
		}
		if(!Cadena.vacio(ordenarPor)) consulta+=" order by " + ordenarPor;
		try {
			int i=0, p=1, par=0;
			Query query=getEm().createQuery(consulta);
			if(propiedades!=null && propiedades.length>0)
				while(par<propiedades.length){	  
					if(tiposConsulta[par].equals(Consulta.IN) || tiposConsulta[i].equals(Consulta.NOT_IN)){ 
						Object[] valoresIn=(Object [])valores[par++];  
						for(int j=0;j<valoresIn.length;j++){
							query.setParameter(p++, valoresIn[j]);
						}
					}
					else query.setParameter(p++, Consulta.obtenerValor(valores[par],tiposConsulta[par++]));
				}	
			if(pagina>0) query.setFirstResult(bloque * (pagina-1)).setMaxResults(bloque);
			return query.getResultList();
		} catch (RuntimeException re) {
			throw re;
		}
	}
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public Integer tamanoTotalListaCargarTodosV2(Class clazz, HashMap<String,Object> parametros) throws Exception{
		return new Integer(cargarTodosV2(clazz, (parametros!=null)?parametros:new HashMap<String,Object>(), -1, Constantes.MAX_RESULTADOS).size());
	}
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public void eliminar(Object objeto, Long id) {
		getEm().remove(getEm().getReference(objeto.getClass(), id));
	}
	
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public void modificar(Object objeto) {
		getEm().merge(objeto);
	}
	
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public EntityManager getEm() {
		return em;
	}
	@PersistenceContext
	public void setEm(EntityManager em) {
		this.em = em;
	}
	
	
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public Integer tamanoTotal(Class clase, String propiedades, ArrayList<Object> valores) {
		StringBuilder sb=new StringBuilder();
		sb.append("select count(c) from "+clase.getName()+" c ");
		Query consulta =null;
		if(propiedades != null && propiedades.trim().length()>0){
			
			sb.append(" where ").append(propiedades);
			consulta = getEm().createQuery(sb.toString());
			int i=1;
			for(Object o:valores){
				consulta.setParameter(i++, o);
			}
		}else consulta = getEm().createQuery(sb.toString());
		 return Integer.valueOf(consulta.getSingleResult().toString());
	}

	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public void eliminar(Object objeto, String id) {
		// TODO Auto-generated method stub
		getEm().remove(getEm().getReference(objeto.getClass(), id));
	}
	
	@Transactional (propagation = Propagation.REQUIRED, readOnly = false)
	public void eliminar(Object objeto) {
		getEm().remove(getEm().merge(objeto));
	}


}
